package org.vdab.projects.tasks.client.util;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import org.vdab.projects.tasks.shared.entities.IBaseEntity;

/**
 * A DelayedList is a collection intended to contain entities. But it might yet not contain them.
 * A DelayedList typically is used as a return value of a synchrony method that will retrieve the entities
 * asynchronicly. The delayed list can be set to execute commands whenever the entities actually arives.
 * 
 * @author stijn
 *
 * @param <CE> The type of the entity contained in the list
 * 
 * 
 * 
 * 
 */
public class DelayedList<CE extends IBaseEntity<?>> implements Set<CE> {

	private HashMap<String, CE> innerList = new HashMap<String, CE>();

	private ICommand<CE> whenLoaded;
	private ICommand<CE> whenFirstEntityAdded;

	public DelayedList() {
		MessageGuard.Log("in DelayedList ctr");
	}

	@SuppressWarnings("unchecked")
	public void setOnFirstEntityAdded(ICommand<CE> whenFirstEntityAdded)
			throws Exception {
		if (this.whenFirstEntityAdded == null) {
			this.whenFirstEntityAdded = whenFirstEntityAdded;
			if (this.innerList.size() > 0) {
				this.whenFirstEntityAdded.execute((CE) this.innerList.values()
						.toArray()[0]);
			}
		} else {
			throw new Exception("setOnFirstEntityAdded may be set only once");
		}
	}

	public void onLoadedDo(ICommand<CE> whenLoaded) throws Exception {
		// If the onLoadedDo is set, it executes it for all the items already in
		// the collection.
		// So it make no difference what happens first, setting of the command
		// of handling it.
		// The whenLoaded command will be executed once for each item added to
		// the list, post or past adding it.
		for (CE item : innerList.values()) {
			MessageGuard
					.Log("Before whenLoaded.execute() in DelatedList.onLoadedDo()");
			whenLoaded.execute(item);
		}
		this.whenLoaded = whenLoaded;
	}

	public void addEntity(CE entity) throws Exception {
		MessageGuard.Log("Adding new Entity to DelayedList. "
				+ entity.getDisplayText());
		if (this.whenFirstEntityAdded != null && innerList.size() == 0) {
			this.whenFirstEntityAdded.execute(entity);
		}

		innerList.put(entity.getKey(), entity);
		if (whenLoaded != null) {
			MessageGuard
					.Log("Before whenLoaded.execute() in DelatedList.addEntity()");
			whenLoaded.execute(entity);

		} else {
			MessageGuard.Log("whenLoaded in DelayedList was NULL");
		}
		MessageGuard.Log("Entity " + entity.getDisplayText() + " added.");
	}

	@Override
	public Iterator<CE> iterator() {
		return innerList.values().iterator();
	}

	public CE getByKey(String key) {
		return innerList.get(key);
	}

	public void addAllEntities(Collection<? extends CE> entities)
			throws Exception {
		for (CE entity : entities) {
			this.addEntity(entity);
		}
	}

	@Override
	public int size() {
		return innerList.size();
	}

	@Override
	public boolean isEmpty() {
		return innerList.isEmpty();
	}

	@Override
	public boolean contains(Object o) {
		return innerList.containsValue(o);
	}

	@Override
	public Object[] toArray() {
		return innerList.values().toArray();
	}

	@Override
	public <T> T[] toArray(T[] a) {
		return innerList.values().toArray(a);
	}

	@Override
	public boolean add(CE e) {
		try {
			this.addEntity(e);
		} catch (Exception ex) {
			MessageGuard.Log(ex.getLocalizedMessage());
		}
		return true;
	}

	@Override
	public boolean remove(Object o) {
		return false;
	}

	@Override
	public boolean containsAll(Collection<?> c) {
		return false;
	}

	@Override
	public boolean addAll(Collection<? extends CE> c) {
		try {
			this.addAllEntities(c);
		} catch (Exception e) {
			MessageGuard.Log(e.getLocalizedMessage());
		}
		return true;
	}

	@Override
	public boolean retainAll(Collection<?> c) {
		return false;
	}

	@Override
	public boolean removeAll(Collection<?> c) {
		for (Object o : c) {
			innerList.remove(o);
		}
		return true;
	}

	@Override
	public void clear() {
		this.innerList.clear();
	}

}
